package com.mask.im.business.aspect;

import com.alibaba.fastjson.JSON;
import com.mask.im.business.common.util.ExceptionUtil;
import com.mask.im.business.common.util.IpUtil;
import com.mask.im.business.mapper.ExceptionLogMapper;
import com.mask.im.business.model.entity.SysExceptionLogEntity;
import io.swagger.annotations.ApiOperation;
import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Date;

/**
 * 异常日志切面
 *
 * @author jian
 */
@Log4j2
@Aspect
@Component
public class ExceptionLogAspect {

    @Resource
    private ExceptionLogMapper exceptionLogMapper;

    @Pointcut("execution(* com.mask.im.business.controller..*.*(..))")
    public void exceptionLogPointcut() {
    }

    @AfterThrowing(value = "exceptionLogPointcut()", throwing = "e")
    public void saveExceptionLog(JoinPoint joinPoint, Exception e) {
        try {
            // 异步保存异常日志，避免影响主业务
            saveExceptionLogAsync(joinPoint, e);
        } catch (Exception ex) {
            log.error("异常日志切面处理异常", ex);
        }
    }

    @Async
    public void saveExceptionLogAsync(JoinPoint joinPoint, Exception e) {
        try {
            // 获取请求信息
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            if (requestAttributes == null) {
                log.warn("无法获取请求上下文，跳过异常日志记录");
                return;
            }

            HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
            if (request == null) {
                log.warn("无法获取HttpServletRequest，跳过异常日志记录");
                return;
            }

            // 构建异常日志实体
            SysExceptionLogEntity logEntity = buildExceptionLog(joinPoint, e, request);

            // 保存到数据库
            exceptionLogMapper.insert(logEntity);

            log.debug("异常日志保存成功，异常: {}, 方法: {}", e.getClass().getSimpleName(), logEntity.getOptMethod());

        } catch (Exception ex) {
            log.error("保存异常日志失败", ex);
        }
    }

    /**
     * 构建异常日志实体
     */
    private SysExceptionLogEntity buildExceptionLog(JoinPoint joinPoint, Exception e, HttpServletRequest request) {
        SysExceptionLogEntity logEntity = new SysExceptionLogEntity();

        try {
            // 获取方法信息
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();

            // 获取API操作注解
            ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);

            // 设置请求信息
            logEntity.setOptUri(request.getRequestURI());
            logEntity.setRequestMethod(request.getMethod());

            // 设置方法信息
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = method.getName();
            logEntity.setOptMethod(className + "." + methodName);

            // 设置操作描述
            if (apiOperation != null) {
                logEntity.setOptDesc(apiOperation.value());
            } else {
                logEntity.setOptDesc("");
            }

            // 设置请求参数
            if (joinPoint.getArgs().length > 0) {
                if (joinPoint.getArgs()[0] instanceof MultipartFile) {
                    logEntity.setRequestParam("file");
                } else {
                    logEntity.setRequestParam(JSON.toJSONString(joinPoint.getArgs()));
                }
            }

            // 设置异常信息
            logEntity.setExceptionInfo(ExceptionUtil.getTrace(e));

            // 设置IP信息
            String ipAddress = IpUtil.getIpAddress(request);
            logEntity.setIpAddress(ipAddress);
            logEntity.setIpSource(IpUtil.getIpSource(ipAddress));

            // 设置时间
            logEntity.setCreateTime(new Date());

        } catch (Exception ex) {
            log.error("构建异常日志实体失败", ex);
        }

        return logEntity;
    }
}